package top.surgeqi.security.jwt.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.StringUtils;
import top.surgeqi.security.jwt.config.extend.ExtendAuthenticationContext;
import top.surgeqi.security.jwt.config.extend.ExtendAuthenticationEntity;
import top.surgeqi.security.jwt.config.extend.ExtendAuthenticator;
import top.surgeqi.security.jwt.contants.AuthConstants;
import top.surgeqi.security.jwt.exception.CustomerAuthenticationException;
import top.surgeqi.security.jwt.exception.UserUnavailableException;

import java.util.List;

/**
 * <p><em>Created by qipp on 2020/6/30 12:54</em></p>
 * 用户详情Service抽象类
 * <p>后续扩展认证方式继承此类即可</p>
 *
 * @author <a href="https://gitee.com/qipengpai">qipp</a>
 * @since 2.0.1
 */
public abstract class AbstractUserDetailsService implements UserDetailsService {

    /**
     * 扩展认证器集合
     */
    @Autowired
    private List<ExtendAuthenticator> authenticators;

    /**
     * 根据用户名（唯一字段）查询用户账户
     *
     * @param username 用户名（唯一字段）
     * @return org.springframework.security.core.userdetails.UserDetails
     * @author qipp
     */
    public abstract UserDetails selectByUserName(String username);

    /**
     * 组合用户账户对象与角色权限
     *
     * @param userDetails 用户账户对象
     * @return org.springframework.security.core.userdetails.UserDetails
     * @author qipp
     */
    public abstract UserDetails composeUserDetailsAndAuthority(UserDetails userDetails);

    /**
     * 验证用户是否可用
     *
     * @param userDetails 用户账户对象
     * @author qipp
     */
    public void validateUser(UserDetails userDetails) {
        if (!userDetails.isAccountNonExpired()) {
            throw new UserUnavailableException("用户已过期！");
        }

        if (!userDetails.isAccountNonLocked()) {
            throw new UserUnavailableException("用户已锁定！");
        }

        if (!userDetails.isEnabled()) {
            throw new UserUnavailableException("用户未启用！");
        }
    }

    /**
     * 根据用户名获取用户详情信息
     *
     * @param username 用户名
     * @return org.springframework.security.core.userdetails.UserDetails
     * @author qipp
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return this.authenticate(username);
    }

    /**
     * 选取合适的认证器认证并认证
     * <p>如果认证通过则返回用户详情，认证没有通过则抛出认证失败的异常</p>
     *
     * @param username 用户名
     * @return org.springframework.security.core.userdetails.UserDetails
     * @author qipp
     */
    private UserDetails authenticate(String username) {
        // 获取认证上下文对象
        ExtendAuthenticationEntity entity = ExtendAuthenticationContext.get();
        UserDetails userDetails;
        if (null == entity) {
            userDetails = this.selectByUserName(username);
            if (null == userDetails) {
                throw new CustomerAuthenticationException("用户不存在！");
            }
            // 验证用户可用性
            this.validateUser(userDetails);
            // 组合用户账户对象与角色权限
            return this.composeUserDetailsAndAuthority(userDetails);
        }

        // 如果用户名为空时则为刷新token 此时赋值username
        if (StringUtils.isEmpty(entity.getAuthParameter(AuthConstants.AUTH_USERNAME))) {
            entity.getAuthParameters().put(AuthConstants.AUTH_USERNAME, new String[]{username});
        }
        if (this.authenticators != null) {
            for (ExtendAuthenticator authenticator : authenticators) {
                if (authenticator.support(entity)) {
                    return authenticator.authenticate(entity);
                }
            }
        }
        throw new CustomerAuthenticationException("未定义的认证器【" + entity.getAuthType() + "】！");
    }
}
